package engine.graphics;

import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas;

import util.HashMap;
import engine.GameEngine;
import engine.Midlet;
import engine.UnitAction;
import engine.data.City;
import engine.data.Map;
import engine.data.Player;
import engine.data.Tile;
import engine.data.Unit;
import engine.data.UnitType;
import engine.graphics.misc.GraphicsCustomFont;
import engine.graphics.popup.PopupManager;

public final class MapCanvas extends GameCanvas {

	public static final short SCREEN_WIDTH = 240;

	public static final short SCREEN_HEIGHT = 288;

	public static final short SCREEN_TILE_WIDTH;

	public static final short SCREEN_TILE_HEIGHT;

	public Graphics graphics;

	public short dX;

	public short dY;

	private static final util.Map ACTION_MAPPING = new HashMap();

	private static final util.Map KEY_MAPPING = new HashMap();

	private static final short ANIM_STEPS = 4;

	private static final short ANIM_SLEEP = 50;

	static {
		SCREEN_TILE_WIDTH = SCREEN_WIDTH / Tile.IMAGE_WIDTH;
		SCREEN_TILE_HEIGHT = SCREEN_HEIGHT / Tile.IMAGE_HEIGHT;

		ACTION_MAPPING.put(new Integer(UP_PRESSED), new Byte(UnitAction.MOVE_UP));
		ACTION_MAPPING.put(new Integer(DOWN_PRESSED), new Byte(UnitAction.MOVE_DOWN));
		ACTION_MAPPING.put(new Integer(LEFT_PRESSED), new Byte(UnitAction.MOVE_LEFT));
		ACTION_MAPPING.put(new Integer(RIGHT_PRESSED), new Byte(UnitAction.MOVE_RIGHT));

		KEY_MAPPING.put(new Integer(KEY_NUM2), new Byte(UnitAction.MOVE_UP));
		KEY_MAPPING.put(new Integer(KEY_NUM1), new Byte(UnitAction.MOVE_UP_LEFT));
		KEY_MAPPING.put(new Integer(KEY_NUM3), new Byte(UnitAction.MOVE_UP_RIGHT));
		KEY_MAPPING.put(new Integer(KEY_NUM8), new Byte(UnitAction.MOVE_DOWN));
		KEY_MAPPING.put(new Integer(KEY_NUM7), new Byte(UnitAction.MOVE_DOWN_LEFT));
		KEY_MAPPING.put(new Integer(KEY_NUM9), new Byte(UnitAction.MOVE_DOWN_RIGHT));
		KEY_MAPPING.put(new Integer(KEY_NUM4), new Byte(UnitAction.MOVE_LEFT));
		KEY_MAPPING.put(new Integer(KEY_NUM6), new Byte(UnitAction.MOVE_RIGHT));

		ACTION_MAPPING.put(new Integer(FIRE_PRESSED), new Byte(UnitAction.FIRE));
		KEY_MAPPING.put(new Integer(KEY_NUM5), new Byte(UnitAction.FIRE));
	}

	public MapCanvas() {
		super(false);
		setFullScreenMode(true);
		this.graphics = getGraphics();
	}

	void hideUnit(final Unit unit) {
		short nx = getScreenPosX(unit.x);
		short ny = getScreenPosY(unit.y);
		if ((nx >= 0) && (nx < SCREEN_TILE_WIDTH) && (ny >= 0) && (ny < SCREEN_TILE_HEIGHT)) {
			Tile tile = GameEngine.map.getTile((short) ((nx + this.dX + GameEngine.map.width) % GameEngine.map.width),
					(short) (ny + this.dY));
			this.graphics.drawImage(tile.terrain.getImage(), nx * Tile.IMAGE_WIDTH, ny * Tile.IMAGE_HEIGHT,
					Graphics.TOP | Graphics.LEFT);
			if (tile.city != null) {
				// tile.city.refreshImage();
				this.graphics.drawImage(tile.city.image, nx * Tile.IMAGE_WIDTH, ny * Tile.IMAGE_HEIGHT, Graphics.TOP
						| Graphics.LEFT);
			}
		}
		this.repaint(nx * Tile.IMAGE_WIDTH, ny * Tile.IMAGE_HEIGHT, Tile.IMAGE_WIDTH, Tile.IMAGE_HEIGHT);
	}

	void showUnit(final Unit unit) {
		short nx = getScreenPosX(unit.x);
		short ny = getScreenPosY(unit.y);
		if ((nx >= 0) && (nx < SCREEN_TILE_WIDTH) && (ny >= 0) && (ny < SCREEN_TILE_HEIGHT)) {
			this.graphics.drawImage(unit.getImage(), nx * Tile.IMAGE_WIDTH, ny * Tile.IMAGE_HEIGHT, Graphics.TOP
					| Graphics.LEFT);
			this.repaint(nx * Tile.IMAGE_WIDTH, ny * Tile.IMAGE_HEIGHT, Tile.IMAGE_WIDTH, Tile.IMAGE_HEIGHT);
		}
	}

	void showCursor(final Unit cursor) {
		short nx = getScreenPosX(cursor.x);
		short ny = getScreenPosY(cursor.y);
		if (GameEngine.map.getTile(cursor.x, cursor.y).image != null)
			this.graphics.drawImage(GameEngine.map.getTile(cursor.x, cursor.y).image, nx * Tile.IMAGE_WIDTH, ny
					* Tile.IMAGE_HEIGHT, Graphics.TOP | Graphics.LEFT);
		this.graphics.drawImage(cursor.getImage(), nx * Tile.IMAGE_WIDTH, ny * Tile.IMAGE_HEIGHT, Graphics.TOP
				| Graphics.LEFT);
		this.repaint(nx * Tile.IMAGE_WIDTH, ny * Tile.IMAGE_HEIGHT, Tile.IMAGE_WIDTH, Tile.IMAGE_HEIGHT);
		cursor.x = (short) ((cursor.x + GameEngine.map.width) % GameEngine.map.width);
	}

	public void hideCursor(final Unit cursor) {
		short nx = getScreenPosX(cursor.x);
		short ny = getScreenPosY(cursor.y);
		Image image = GameEngine.map.getTile(cursor.x, cursor.y).image;
		this.graphics.setColor(0, 0, 0);
		if (image != null)
			this.graphics.drawImage(image, nx * Tile.IMAGE_WIDTH, ny * Tile.IMAGE_HEIGHT, Graphics.TOP | Graphics.LEFT);
		else
			this.graphics.fillRect(nx * Tile.IMAGE_WIDTH, ny * Tile.IMAGE_HEIGHT, Tile.IMAGE_WIDTH, Tile.IMAGE_HEIGHT);
		this.repaint(nx * Tile.IMAGE_WIDTH, ny * Tile.IMAGE_HEIGHT, Tile.IMAGE_WIDTH, Tile.IMAGE_HEIGHT);
	}

	void moveUnit(final Player player, final Unit unit, final short unitDX, final short unitDY) {
		short steps = ANIM_STEPS;
		short x = getScreenPosX(unit.x);
		short y = getScreenPosY(unit.y);
		if (autoScroll(x, y, unitDX, unitDY)) {
			x = getScreenPosX(unit.x);
			y = getScreenPosY(unit.y);
		}
		int nx = x * Tile.IMAGE_WIDTH;
		int ny = y * Tile.IMAGE_HEIGHT;
		int inx = nx;
		int iny = ny;
		int stepX = (unitDX * Tile.IMAGE_WIDTH) / steps;
		int stepY = (unitDY * Tile.IMAGE_HEIGHT) / steps;
		int width = Tile.IMAGE_WIDTH + Math.abs(stepX);
		int height = Tile.IMAGE_HEIGHT + Math.abs(stepY);
		Tile tile = GameEngine.map.getTile(unit.x, unit.y);
		Image background = tile.getImage(player, false);
		for (short i = 1; i <= steps; i++) {
			this.graphics.drawImage(background, inx, iny, Graphics.TOP | Graphics.LEFT);
			this.graphics.drawImage(unit.getImage(), nx + stepX, ny + stepY, Graphics.TOP | Graphics.LEFT);
			this.repaint(Math.min(nx, nx + stepX), Math.min(ny, ny + stepY), width, height);
			nx += stepX;
			ny += stepY;
			if (i < steps) {
				try {
					Thread.sleep(ANIM_SLEEP);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		this.graphics.drawImage(tile.image, x * Tile.IMAGE_WIDTH, y * Tile.IMAGE_HEIGHT, Graphics.TOP | Graphics.LEFT);
		this.repaint(nx, ny, width, height);
	}

	public void refreshUnitRange(final Unit unit) {
		refreshRange(unit.x, unit.y, UnitType.getRP(unit.type));
	}

	void refreshCityRange(final City city) {
		refreshRange(city.x, city.y, (byte) 1);
	}

	void moveCursor(final Unit cursor, final short unitDX, final short unitDY) {
		if ((dX != 0 || dY != 0) && (cursor.y + unitDY >= 0 && cursor.y + unitDY < GameEngine.map.height)) {
			short x = getScreenPosX(cursor.x);
			short y = getScreenPosY(cursor.y);
			Tile tile = GameEngine.map.getTile(cursor.x, cursor.y);
			this.graphics.setColor(0, 0, 0);
			if (tile.image != null)
				this.graphics.drawImage(tile.image, x * Tile.IMAGE_WIDTH, y * Tile.IMAGE_HEIGHT, Graphics.TOP
						| Graphics.LEFT);
			else
				this.graphics
						.fillRect(x * Tile.IMAGE_WIDTH, y * Tile.IMAGE_HEIGHT, Tile.IMAGE_WIDTH, Tile.IMAGE_HEIGHT);
			autoScroll(x, y, unitDX, unitDY);
			short minX = (short) Math.min(getScreenPosX(cursor.x), getScreenPosX((short) (cursor.x + unitDX)));
			short minY = (short) Math.min(getScreenPosY(cursor.y), getScreenPosY((short) (cursor.y + unitDY)));
			short maxX = (short) Math.max(getScreenPosX(cursor.x), getScreenPosX((short) (cursor.x + unitDX)));
			short maxY = (short) Math.max(getScreenPosY(cursor.y), getScreenPosY((short) (cursor.y + unitDY)));
			cursor.move(unitDX, unitDY, (short) 0);
			showUnit(cursor);
			cursor.x = (short) ((cursor.x + GameEngine.map.width) % GameEngine.map.width);
			this.repaint(minX * Tile.IMAGE_WIDTH, minY * Tile.IMAGE_HEIGHT, (maxX - minX + 1) * Tile.IMAGE_WIDTH, (maxY
					- minY + 1)
					* Tile.IMAGE_HEIGHT);
		}
	}

	void scroll(final short screenDX, final short screenDY) {
		this.dX = (short) ((this.dX + screenDX) % GameEngine.map.width);
		if (this.dY + screenDY <= (GameEngine.map.height - SCREEN_TILE_HEIGHT) && this.dY + screenDY >= 0)
			this.dY = (short) (this.dY + screenDY);
		displayMap();
	}

	public void displayMap() {
		int nx;
		int ny;
		short x;
		short y;
		Image image;
		this.graphics.setColor(0, 0, 0);
		Map map = GameEngine.map;
		for (short i = 0; i < SCREEN_TILE_WIDTH; i++)
			for (short j = 0; j < SCREEN_TILE_HEIGHT; j++) {
				x = (short) ((i + dX + GameEngine.map.width) % GameEngine.map.width);
				y = (short) (j + dY);
				nx = i * Tile.IMAGE_WIDTH;
				ny = j * Tile.IMAGE_HEIGHT;
				image = map.getImage(x, y);
				if (image != null)
					this.graphics.drawImage(image, nx, ny, Graphics.TOP | Graphics.LEFT);
				else
					this.graphics.fillRect(nx, ny, Tile.IMAGE_WIDTH, Tile.IMAGE_HEIGHT);
			}
		for (short i = 0; i < SCREEN_TILE_WIDTH; i++)
			for (short j = 0; j < SCREEN_TILE_HEIGHT; j++) {
				x = (short) ((i + dX + GameEngine.map.width) % GameEngine.map.width);
				y = (short) (j + dY);
				nx = i * Tile.IMAGE_WIDTH;
				ny = j * Tile.IMAGE_HEIGHT;
				if (map.getCity(x, y) != null)
					GraphicsCustomFont.drawString(this.graphics, map.getCity(x, y).name, nx, ny + Tile.IMAGE_HEIGHT);
			}
		this.repaint(0, 0, SCREEN_TILE_WIDTH * Tile.IMAGE_WIDTH, SCREEN_HEIGHT * Tile.IMAGE_HEIGHT);
	}

	public void autocenter(final Unit unit) {
		if (unit != null) {
			short x = getScreenPosX(unit.x);
			short y = getScreenPosY((short) (Math.max((SCREEN_TILE_HEIGHT / 2), Math.min(unit.y, GameEngine.map.height
					- (SCREEN_TILE_HEIGHT / 2) - (SCREEN_TILE_HEIGHT % 2)))));
			short screenDX = 0;
			short screenDY = 0;
			if (x >= SCREEN_TILE_WIDTH || x < 0)
				screenDX = (short) (x - SCREEN_TILE_WIDTH + (SCREEN_TILE_WIDTH / 2));
			if (y >= SCREEN_TILE_HEIGHT || y < 0)
				screenDY = (short) (y - (SCREEN_TILE_HEIGHT / 2));
			if (screenDX != 0 || screenDY != 0)
				scroll(screenDX, screenDY);
		}
	}

	protected void keyRepeated(final int key) {
		if (hasRepeatEvents())
			keyPressed(key);
	}

	protected void keyPressed(final int key) {
		if (PopupManager.popup == null || !PopupManager.popup.isVisible) {
			if (Midlet.canvasEngine != null) {
				boolean played = false;
				switch (key) {
					case KEY_NUM2:
					case KEY_NUM4:
					case KEY_NUM6:
					case KEY_NUM8:
					case KEY_NUM1:
					case KEY_NUM3:
					case KEY_NUM7:
					case KEY_NUM9:
					case KEY_NUM5:
						Midlet.canvasEngine.addAction(((Byte) KEY_MAPPING.get(new Integer(key))).byteValue());
						played = true;
						break;
					default:
						break;
				}
				if (!played) {
					int action = getKeyStates();
					if (action != 0)
						switch (action) {
							case LEFT_PRESSED:
							case RIGHT_PRESSED:
							case UP_PRESSED:
							case DOWN_PRESSED:
							case FIRE_PRESSED:
								Midlet.canvasEngine.addAction(((Byte) ACTION_MAPPING.get(new Integer(action)))
										.byteValue());
								break;
							default:
								break;
						}
				}
			}
		} else
			PopupManager.popup.keyPressed(key);
	}

	private boolean autoScroll(final short x, final short y, final short unitDX, final short unitDY) {
		short screenDX = 0;
		short screenDY = 0;
		if (x + unitDX >= (SCREEN_TILE_WIDTH - 1) || x + unitDX <= 0)
			screenDX = unitDX;
		if (y + unitDY >= (SCREEN_TILE_HEIGHT - 1) || y + unitDY <= 0)
			screenDY = unitDY;
		if (screenDX != 0 || screenDY != 0) {
			scroll(screenDX, screenDY);
			return true;
		}
		return false;
	}

	private short getScreenPosX(final short x) {
		return (short) ((x - this.dX + GameEngine.map.width) % GameEngine.map.width);
	}

	private short getScreenPosY(final short y) {
		return (short) (y - this.dY);
	}

	public void refreshRange(final short posX, final short posY, final byte range) {
		int nx = getScreenPosX(posX);
		int ny = getScreenPosY(posY);
		int x;
		int y;
		int x2;
		int y2;
		for (byte i = ((byte) -range); i <= range; i++)
			for (byte j = ((byte) -range); j <= range; j++)
				if (posY + j >= 0 && posY + j < GameEngine.map.height) {
					x = nx + i;
					y = ny + j;
					if (x >= 0 && y >= 0 && x < SCREEN_TILE_WIDTH && y < SCREEN_TILE_HEIGHT) {
						Image image = GameEngine.map.getTile(
								(short) ((posX + i + GameEngine.map.width) % GameEngine.map.width), (short) (posY + j)).image;
						if (image != null)
							this.graphics.drawImage(image, x * Tile.IMAGE_WIDTH, y * Tile.IMAGE_HEIGHT, Graphics.TOP
									| Graphics.LEFT);
					}
				}
		x = Math.max(0, (nx - range) * Tile.IMAGE_WIDTH);
		x2 = Math.min(SCREEN_TILE_WIDTH * Tile.IMAGE_WIDTH, (nx + range + 1) * Tile.IMAGE_WIDTH);
		y = Math.max(0, (ny - range) * Tile.IMAGE_HEIGHT);
		y2 = Math.min(SCREEN_TILE_HEIGHT * Tile.IMAGE_HEIGHT, (ny + range + 1) * Tile.IMAGE_HEIGHT);
		this.repaint(x, y, x2, y2);
	}
}
